const {
  db,
  bodyParser,
  md5,
  dbPromise,
  redis_client,
  redis_prefix,
} = require("../unit");

class Admin {
  constructor(info) {
    this.info = info;
  }
  toJSON() {
    const { info } = this;
    return {
      nickname: info.nickname,
      create_time: info.create_time,
    };
  }
}
exports.Admin = Admin;

class User {
  constructor(info, id) {
    for (let key in info) {
      this[key] = info[key];
    }
    this.id = id || md5(info.openid);
    if (!isFinite(this.points)) {
      this.points = 0;
    }
    if (!isFinite(this.cards)) {
      this.cards = 0;
    }
  }
  static cutCards(user_id, cut_num = 1) {
    return _changeUserAsset(user_id, "cards", "cut", cut_num);
  }
  static cutPoints(user_id, cut_num = 1) {
    return _changeUserAsset(user_id, "points", "cut", cut_num);
  }
  static addPoints(user_id, add_num = 1) {
    return _changeUserAsset(user_id, "points", "add", add_num);
  }
  static addBotimesAndLog(user_id, botime_time, log) {
    return Promise.all(
      _changeUserAsset(user_id, "botimes", "add", 1),
      db.ref("botimes").push({
        user_id,
        botime_time,
        log: JSON.stringify(log),
      })
    );
  }
  static async getGameLogs(user_id, from_create_time, limit, options) {
    options || (options = {});
    const sort_by = "create_time";
    let map;
    if (from_create_time) {
      map = await dbPromise(
        db
          .ref("user_logs/" + user_id)
          .orderByChild(sort_by)
          .endAt(from_create_time, sort_by)
          .limitToLast(limit),
        {
          can_null: true,
        }
      );
    } else {
      map = await dbPromise(
        db.ref("user_logs/" + user_id).orderByChild(sort_by).limitToLast(limit),
        {
          can_null: true,
        }
      );
    }
    if (!map) {
      return {
        list: [],
      };
    }
    const noop = function() {};
    noop.prototype = map;
    const map_keys = Object.keys(map);

    const list = [];
    for (var i = 0, last_i = map_keys.length - 1; i <= last_i; i += 1) {
      var map_key = map_keys[i];
      list[last_i - i] = new User(map[map_key], map_key);
    }

    list.sort((a, b) => b[sort_by] - a[sort_by]);

    const res_list = [];
    await Promise.all(
      list.map(async item => {
        res_list.push(await dbPromise(db.ref("room_logs/" + item.room_id)));
      })
    );
    return {
      list: res_list,
    };
  }
}
User.setting = {
  init_points: 0, // 注册用户初始化的水豆
  init_cards: 0, // 注册用户初始化的房卡
  share_return_points: 0, // 分享后给分享人的奖励水豆
  add_1_cards_return_points: 0, // 充值房卡附加的水豆
};
exports.User = User;

const admin_db = db.ref("admins");
const usersRef = db.ref("users");
async function _changeUserAsset(user_id, asset_type, change_mode, value) {
  const user = await dbPromise(usersRef.child(user_id), {
    null_errormsg: "找不到指定用户",
  });

  const format_value = isFinite(value) ? parseFloat(value) : 0;

  const { committed, snapshot } = await usersRef
    .child(user_id)
    .child(asset_type)
    .transaction(function(current_value) {
      if (change_mode === "change") {
        return format_value;
      } else if (change_mode === "add") {
        return (current_value || 0) + format_value;
      } else if (change_mode === "cut") {
        return (current_value || 0) - format_value;
      }
    });

  if (committed) {
    user[asset_type] = snapshot.val();
    /* 业务情况 */
    // 充房卡送水豆
    if (asset_type === "cards" && change_mode === "add") {
      _changeUserAsset(
        user_id,
        "points",
        "add",
        value * User.setting.add_1_cards_return_points
      );
    }
  }

  return new User(user, user_id);
}

exports.setup = function(router) {
  const getAdminLoginer = async ctx => {
    const login_session = ctx.cookies.get("admin-login-session", {
      signed: true,
    });
    if (!login_session) {
      throw "管理员未登录";
    }
    const loginer_admin_info_json = await redis_client.getAsync(
      redis_prefix + "admin-session-" + login_session
    );
    if (!loginer_admin_info_json) {
      throw "管理员登录超时";
    }
    try {
      const loginer_admin_info = JSON.parse(loginer_admin_info_json);
      return new Admin(loginer_admin_info);
    } catch (e) {
      throw "管理员信息有误，请重新登录";
    }
  };

  /**
   * 获取已经登录的管理员信息
   */
  router.get("/admin/loginer", async (ctx, next) => {
    const admin = await getAdminLoginer(ctx);
    const admin_io_token = md5("ADMIN-IO-TOKEN" + Date.now() + Math.random());
    await redis_client.writeStringAsync(
      redis_prefix + "ADMIN-IO-TOKEN",
      admin_io_token
    );
    ctx.body = { admin, admin_io_token };
  });
  /**
   * 管理员登录
   */
  router.post("/admin/login", async (ctx, next) => {
    const form = await bodyParser(ctx);
    const { username: nickname, password: password } = form;
    if (!(nickname && password)) {
      throw "参数不完整";
    }
    const md55pwd = md5(md5(password) + password);
    const find_admins = await dbPromise(
      admin_db.orderByChild("nickname").equalTo(nickname),
      {
        null_errormsg: "登录用户名无效",
      }
    );
    console.log("find_admins",find_admins)
    for (let _id in find_admins) {
      const admin = find_admins[_id];
      if (admin.md55pwd === md55pwd) {
        const login_session = (Date.now() + Math.random()).toString(36);
        ctx.cookies.set("admin-login-session", login_session, {
          signed: true,
        });
        await redis_client.writeObjectAsync(
          redis_prefix + "admin-session-" + login_session,
          admin
        );
        ctx.body = "登录成功";
        return;
      }
    }
    throw "登录密码有误";
  });
  /**
   * 获取用户列表
   */
  router.get("/admin/user/list", async (ctx, next) => {
    await getAdminLoginer(ctx); // 管理员登录校验
    const { page, num, sort_by = "create_time" } = ctx.query;
    const _page = (parseInt(page) || 1) - 1;
    const _num = parseInt(num) || 10;
    const _from = _page * _num;
    const _to = _from + _num;
    let list;

    if (_from) {
      const pre_map = await dbPromise(
        usersRef.orderByChild(sort_by).limitToLast(_from)
      );

      const pre_map_noop = function() {};
      pre_map_noop.prototype = pre_map;

      const pre_map_keys = Object.keys(pre_map);
      let min_sort_by = Infinity;
      const min_item_key_set = new Set();
      for (var i = 0; i < pre_map_keys.length; i += 1) {
        var pre_map_key = pre_map_keys[i];
        var pre_map_item_sort_by = pre_map[pre_map_key][sort_by];
        if (pre_map_item_sort_by < min_sort_by) {
          min_sort_by = pre_map_item_sort_by;
          min_item_key_set.clear();
          min_item_key_set.add(pre_map_key);
        } else if (pre_map_item_sort_by === min_sort_by) {
          min_item_key_set.add(pre_map_key);
        }
      }

      const min_item_key_set_size = min_item_key_set.size;

      const map = await dbPromise(
        usersRef
          .orderByChild(sort_by)
          .endAt(min_sort_by, sort_by)
          .limitToLast(_num + min_item_key_set_size)
      );

      const map_noop = function() {};
      map_noop.prototype = map;
      const map_keys = Object.keys(map);

      const res_list = [];
      for (var i = 0; i < map_keys.length; i += 1) {
        var map_key = map_keys[i];
        if (!min_item_key_set.has(map_key)) {
          res_list[res_list.length] = new User(map[map_key], map_key);
        }
      }

      list = res_list;
    } else {
      const map = await dbPromise(
        usersRef.orderByChild(sort_by).limitToLast(_to)
      );
      const noop = function() {};
      noop.prototype = map;
      const map_keys = Object.keys(map);

      const res_list = [];
      for (var i = 0, last_i = map_keys.length - 1; i <= last_i; i += 1) {
        var map_key = map_keys[i];
        res_list[last_i - i] = new User(map[map_key], map_key);
      }
      list = res_list;
    }

    list.sort((a, b) => b[sort_by] - a[sort_by]);
    ctx.body = {
      page: _page + 1,
      num: _num,
      list,
    };
  });
  router.get("/admin/user/list/total", async (ctx, next) => {
    await getAdminLoginer(ctx);
    const total = await dbPromise(db.ref("counter/users"));
    ctx.body = {
      total,
    };
  });

  const search_text_format = {
    sex(search_text) {
      if (search_text == "男") {
        return 1;
      }
      if (search_text == "女") {
        return 2;
      }
      return search_text;
    },
  };

  router.get("/admin/user/search", async (ctx, next) => {
    await getAdminLoginer(ctx);
    const {
      search_text,
      search_type = "all",
      limit_num = Infinity,
    } = ctx.query;
    if (!search_text) {
      throw "搜索文本不可为空";
    }
    const format_search_text = search_text.trim();
    if (!format_search_text) {
      throw "搜索文本不可为纯空格";
    }
    const res_list = new Set();

    let all_user_list_gen = null;
    const getAllUserGen = async () => {
      if (!all_user_list_gen) {
        all_user_map = await dbPromise(usersRef);
        const map_noop = function() {};
        map_noop.prototype = all_user_map;
        const map_keys = Object.keys(all_user_map);
        const all_user_list = [];

        all_user_list_gen = function*() {
          for (var i = 0; i < map_keys.length; i += 1) {
            const map_key = map_keys[i];
            const user_item = all_user_map[map_key];
            yield (all_user_list[all_user_list.length] = new User(
              user_item,
              map_key
            ));
          }
          all_user_list_gen = function*() {
            for (var i = 0; i < all_user_list.length; i += 1) {
              yield all_user_list[i];
            }
          };
        };
      }
      return all_user_list_gen();
    };
    const search_by_id = async () => {
      const all_user_list = await getAllUserGen();
      for (let user of all_user_list) {
        if (user.id === search_text || user.id.indexOf(search_text) !== -1) {
          res_list.add(user);
          if (limit_num <= res_list.size) {
            break;
          }
        }
      }
    };
    if (search_type === "id") {
      await search_by_id();
    }
    for (let type of ["nickname", "city", "province", "sex"]) {
      if (search_type === "all" || search_type === type) {
        let search_values = format_search_text;
        if (search_text_format.hasOwnProperty(type)) {
          search_values = search_text_format[type](format_search_text);
        }
        if (!Array.isArray(search_values)) {
          search_values = [search_values];
        }
        for (let search_value of search_values) {
          const all_user_list = await getAllUserGen();
          for (let user of all_user_list) {
            const user_value = user[type];
            if (
              user_value == search_value ||
              (typeof user_value === "string" &&
                typeof search_value === "string" &&
                (user_value.indexOf(search_value) !== -1 ||
                  user_value
                    .toLowerCase()
                    .indexOf(search_value.toLowerCase()) !== -1))
            ) {
              res_list.add(user);
              if (limit_num <= res_list.size) {
                break;
              }
            }
          }
        }
      }

      if (limit_num <= res_list.size) {
        break;
      }
    }
    if (search_type === "all") {
      search_by_id();
    }
    console.log(limit_num, res_list.size, limit_num <= res_list.size);
    ctx.body = {
      search_type,
      searched_list: [...res_list],
    };
  });

  /**
   * 修改水豆
   */
  for (let __key of ["points", "cards"]) {
    router.put("/admin/user/" + __key, async (ctx, next) => {
      await getAdminLoginer(ctx);
      const { user_id, change_mode = "change", value = 0 } = ctx.query;
      if (!user_id) {
        throw "参数错误";
      }

      ctx.body = await _changeUserAsset(user_id, __key, change_mode, value);
    });
  }
};
